#include "stdafx.h"
#include <boost/test/auto_unit_test.hpp>
#include "bmock.hpp"
#include <boost/thread.hpp>

static const int x = 1;
static int y = 2;
static int yy;

struct tester2 {
	tester2() {
		BMOCK_CREATE_MOCK(*);		
		yy = 0;
	}
	static void m();
};

static int f(int x, int& y) {
	MOCK(2, (IN(x), OUT(y)));
	return 1;
}
static void g() {
	MOCK_VOID(0,());
}
void tester2::m() {
	MOCK_VOID(0,());
}

struct callablef12
{
	void operator()() {
		f(x,yy);
		tester2::m();
	};
};

struct callablef22
{
	void operator()() {
		f(x+1,yy);
	};
};

struct callableg2
{
	void operator()() {
		g();
	};
};


BMOCK_TEST(tester2, test_mutex_with_many_threads) {	
	const int times = 100;
	callablef12 cl;

	BMOCK_NON_STRICT {
		for (int i = 0; i < times; i++) {
			BMOCK_EXPECT_RETURN(i,f(x, y));
			BMOCK_EXPECT(m());
		}
	}
	BMOCK_REPLAY;	
	boost::thread thrd[times];
	for (int i = 0; i<times; i++) {
		thrd[i] = boost::thread(cl);
	}
	for (int i = 0; i<times; i++) {
		thrd[i].join();
	}
	BOOST_CHECK_EQUAL(y,yy);
	BMOCK_VERIFY;
}

BMOCK_TEST(tester2, test_thread_combined_with_non_strict) {
	callablef12 clf1;
	callablef22 clf2;
	callableg2 clg;

	BMOCK_NON_STRICT {
		BMOCK_EXPECT_RETURN(0,f(x,y));			//each expectation is called by different thread
		BMOCK_EXPECT(m());
		BMOCK_EXPECT_RETURN(0,f(x+1,y));		
		BMOCK_EXPECT(g());
	}
	BMOCK_REPLAY;
	boost::thread thrd1 = boost::thread(clf1);
	boost::thread thrd2 = boost::thread(clf2);
	boost::thread thrd3 = boost::thread(clg);
	thrd1.join();
	thrd2.join();
	thrd3.join();
	BOOST_CHECK_EQUAL(y,yy);
	BMOCK_VERIFY;
}

//same as previous test just reversed order
BMOCK_TEST(tester2, test_thread_combined_with_non_strict2) {
	callablef12 cl;
	callablef22 cl2;
	callableg2 clg;

	BMOCK_NON_STRICT {
		BMOCK_EXPECT_RETURN(0,f(x,y));
		BMOCK_EXPECT(m());
		BMOCK_EXPECT_RETURN(0,f(x+1,y));
		BMOCK_EXPECT(g());
	}
	BMOCK_REPLAY;
	boost::thread thrd3 = boost::thread(clg);
	boost::thread thrd2 = boost::thread(cl2);
	boost::thread thrd1 = boost::thread(cl);	
	thrd3.join();
	thrd2.join();
	thrd1.join();	
	BOOST_CHECK_EQUAL(y,yy);
	BMOCK_VERIFY;
}




